/*
 * User: user
 * Date: 12/12/2007
 * Time: 20:46:19
 */
package org.conann.metadata;

import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import org.conann.exceptions.WebBeansMetadataException;
import org.conann.metadata.records.MethodParameter;

import javax.webbeans.types.BindingType;
import javax.webbeans.types.DeploymentType;
import javax.webbeans.types.ScopeType;
import javax.webbeans.types.Stereotype;
import static java.lang.String.format;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import static java.lang.annotation.ElementType.*;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;

public enum AnnotationType {

    binding(BindingType.class, METHOD, FIELD, PARAMETER, TYPE),
    scope(ScopeType.class, METHOD, TYPE),
    deployment(DeploymentType.class, TYPE, METHOD),
    stereotype(Stereotype.class, TYPE, METHOD);

    private final Class<? extends Annotation> type;
    private final Set<ElementType> allowedElements;

    @SuppressWarnings({"AssignmentToCollectionOrArrayFieldFromParameter"})
    AnnotationType(final Class<? extends Annotation> type, ElementType... allowedElements) {
        this.type = type;
        this.allowedElements = Sets.newEnumSet(Arrays.asList(allowedElements), ElementType.class);
    }

    public Class<? extends Annotation> getType() {
        return type;
    }

    public static AnnotationType getAnnotationType(Annotation annotation) {
        checkNotNull(annotation);
        return getAnnotationType(annotation.annotationType());
    }

    public static AnnotationType getAnnotationType(final Class<? extends Annotation> type) {
        checkNotNull(type);
        for (Annotation metaAnnotation : type.getAnnotations()) {
            for (AnnotationType annotationType : values()) {
                if (annotationType.getType().equals(metaAnnotation.annotationType())) {
                    return annotationType;
                }
            }
        }
        return null;
    }

    public boolean isAllowedFor(AnnotatedElement annotatedElement) {
        ElementType elementType = getElementType(annotatedElement);
        return allowedElements.contains(elementType);
    }

    public boolean is(Class<? extends Annotation> annotataionType) {
        return equals(getAnnotationType(annotataionType));
    }

    private ElementType getElementType(AnnotatedElement element) throws WebBeansMetadataException {
        if (element instanceof Class<?>) {
            return TYPE;
        } else if (element instanceof Method) {
            return METHOD;
        } else if (element instanceof Field) {
            return FIELD;
        } else if (element instanceof MethodParameter) {
            return PARAMETER;
        }
        throw new WebBeansMetadataException(format("Web Beans does not support annotating elements of type %s", element));
    }

    public Set<ElementType> getAllowedElements() {
        return allowedElements;
    }
}